/*******************************************************************************
 * Copyright (c) 2018 xxxxxx Corp.
 *
 * All rights reserved.
 *
 * Contributors:
 *    mfqing - initial,20190823
 *******************************************************************************/

/**
 * @file    snmplib.c
 * @brief   .
 *
 */

/*===========================================================================*/
/* standard headers.                                                   */
/*===========================================================================*/
#include "my_include.h"

/*===========================================================================*/
/* local headers.                                                            */
/*===========================================================================*/

/*===========================================================================*/
/* macros.                                                                   */
/*===========================================================================*/

/*===========================================================================*/
/* constants.                                                                */
/*===========================================================================*/

/*===========================================================================*/
/* local definitions.                                                        */
/*===========================================================================*/

/*===========================================================================*/
/* exported variables.                                                       */
/*===========================================================================*/

/*===========================================================================*/
/* local functions.                                                          */
/*===========================================================================*/




u8 idLen=0;


static uint32_t Str2int(const char *ostr)
{
    uint32_t id = 0;
    char *pcurr;
    pcurr = (char *)ostr;
    if (*pcurr == '.'){
        pcurr++;
    }
    id = atoi(pcurr);
    return id;
}
static uint8_t int2BERBuff(const uint32_t mint, uint8_t buff[])
{
    uint8_t i = 0;
    uint32_t value = mint;
    if (value < 128){
        value %= 128;
        buff[i++] = value;
    }
    else if (value < 128*128){
        buff[i++] = (value / 128) | 0x80;
        value %= 128;
        buff[i++] = value;
    }
    else if (value < 128*128*128){
        buff[i++] = (value / 128 /128) | 0x80;
        value %= 128*128;
        buff[i++] = (value / 128) | 0x80;
        value %= 128;
        buff[i++] = value;
    }
    else if (value < 128*128*128*128){
        buff[i++] = (value / 128 / 128 / 128) | 0x80;
        value %= 128*128*128;
        buff[i++] = (value / 128/128) | 0x80;
        value %= 128*128;
        buff[i++] = (value / 128) | 0x80;
        value %= 128;
        buff[i++] = value;
    }
    return i;
}
static uint8_t int2buff(const uint32_t mint, uint8_t buff[])
{
    uint8_t i = 0;
    uint32_t value = mint;
//    if(value < 128)
//    {
//            buff[i++] = value;
//    }    
    if(value < 256)
    {
        buff[i++] = 0;
        buff[i++] = value;
    }
    else if(value < 256*256){
        buff[i++] = value >> 8;
        buff[i++] = value &  0xFF;
    }
    else if(value < 128*128*128){
        buff[i++] = value >> 16;
        buff[i++] = value >> 8;
        buff[i++] = value &  0xFF;
    }
    else if(value < 128*128*128*128){
        buff[i++] = value >> 24;
        buff[i++] = value >> 16;
        buff[i++] = value >> 8;
        buff[i++] = value &  0xFF;
    }
    return i;
}

static uint8_t BERBuff2int(const uint8_t buff[], const uint8_t len, uint32_t *value)
{
    *value = 0;
    uint8_t i;
    for(i = 0; i < len; i++){
        *value += buff[i] & 0x7F;
        if (buff[i] & 0x80){
            *value *= 128;
        }
        else{
            i++;
            break;
        }
    }
    return i;
}
static uint16_t oidName2Buff(const char *ostr, uint8_t data[])
{
    uint32_t id[SNMP_MAX_OID];
    uint8_t cnt=0;
    uint8_t i,j;
    char *pcurr;
    pcurr = (char *)ostr;
    i = 0;
    while(pcurr != NULL){
        id[i++] = Str2int(pcurr);
        pcurr = strchr(pcurr+1, '.');
        cnt++;
    }
    i = 0;
    if(cnt >= 2){
        data[i++] = id[0]*40 + id[1];
        for (j = 2; j < cnt; j++){
                i += int2BERBuff(id[j], data+i);
        }
    }
    else if(cnt ==1){
        data[i++] = id[0];
    }
    return i;
}
static uint16_t parseLength(const uint8_t *msg, uint16_t *len)
{
    uint16_t i=1;

    if (msg[0] & 0x80){
        int32_t tlen = (msg[0] & 0x7f) - 1;
        *len = msg[i++];
        while (tlen--){
            *len <<= 8;
            *len |= msg[i++];
        }
    }
    else{
        *len = msg[0];
    }

    return i;
}
static bool checkTLV(const tlv_t *tlv)
{
    switch(tlv->tag){
        case VALTYPE_BOOL:
        case VALTYPE_INT:
        case VALTYPE_OCTSTR:
        case VALTYPE_NULL:
        case BTAG_OBJID:
        case VALTYPE_ENUM:
        case BTAG_SEQ:
        case BTAG_SETOF:
        case VALTYPE_IPADDR:
        case BTAG_COUNTER:
        case BTAG_GAUGE:
        case VALTYPE_TIMETICKS:
        case BTAG_OPAQUE:
        case PDUTYPE_GET:
        case PDUTYPE_GETNEXT:
        case PDUTYPE_RESP:
        case PDUTYPE_SET:
        case PDUTYPE_TRAP_V1:
        case PDUTYPE_TRAP_V2C:{
            return TRUE;
        }
    }
    return FALSE;
}
static uint16_t Buff2TLV(const uint8_t buff[], uint16_t len, tlv_t *tlv)
{
    uint16_t idx = 0;
    uint16_t tsize = 0;

    tlv->tag = buff[idx++];
    idx += parseLength(buff+idx, &tlv->len);
    if ((idx + tlv->len) <= len){
        memcpy(tlv->data, buff+idx, tlv->len);
        tsize = idx + tlv->len;
    }
    return tsize;
}
static uint16_t TLV2Buff(const tlv_t *tlv, uint8_t buff[])
{
    uint16_t i = 0;
    buff[i++] = tlv->tag;
    if(tlv->len <= 0x7F){
        buff[i++] = tlv->len;
    }
    else{
        if(tlv->len <= 0xFF){
            buff[i++] = 0x80 | 1;
            buff[i++] = tlv->len;
        }
        else if(tlv->len < 0xFFFF){
            buff[i++] = 0x80 | 2;
            buff[i++] = tlv->len >> 8;
            buff[i++] = tlv->len & 0xFF;
        }
    }
    memcpy(buff+i, tlv->data, tlv->len);
    i += tlv->len;
    return i;
}
static void setBool2TLV(const bool value, tlv_t *tlv)
{
    tlv->tag = VALTYPE_BOOL;
    tlv->len = int2BERBuff(value, tlv->data);
}
static void setInteger2TLV(const uint32_t value, tlv_t *tlv)
{
    tlv->tag = VALTYPE_INT;
    tlv->len = int2buff(value, tlv->data);
}
static void setString2TLV(const char *str, tlv_t *tlv)
{
    uint16_t len;
    len = strlen(str);
    tlv->tag = VALTYPE_OCTSTR;
    tlv->len = MIN(len, SNMP_MAX_STRING);
    memcpy(tlv->data, str, len);
}
static void setNull2TLV(tlv_t *tlv)
{
    tlv->tag = VALTYPE_NULL;
    tlv->len = 0;
}
static void setOidName2TLV(const char *oidname, tlv_t *tlv)
{
    tlv->tag = BTAG_OBJID;
    tlv->len = oidName2Buff(oidname, tlv->data);
}
static void setVersion2TLV(const uint8_t ver, tlv_t *tlv)
{
    setInteger2TLV(ver, tlv);
}
static void setCommunity2TLV(const char *cstr, tlv_t *tlv)
{
    setString2TLV(cstr, tlv);
}
static void setEnterprise2TLV(const char *enterprise, tlv_t *tlv)
{
    tlv->tag = BTAG_OBJID;
    tlv->len = oidName2Buff(enterprise, tlv->data);
}
static void setIpaddr2TLV(const uint8_t *ipaddr, tlv_t *tlv)
{
    uint8_t i;
    tlv->tag = VALTYPE_IPADDR;
    tlv->len = 4;
    for (i = 0; i < tlv->len; i++){
        tlv->data[i] = ipaddr[i];
    }
}
static void setTrapType2TLV(const uint8_t type, tlv_t *tlv)
{
    setInteger2TLV(type, tlv);
}
static void setTrapSpecific2TLV(const uint8_t specific, tlv_t *tlv)
{
    setInteger2TLV(specific, tlv);
}
static void setErrorState2TLV(const uint8_t state, tlv_t *tlv)
{
    setInteger2TLV(state, tlv);
}
static void setErrorIndex2TLV(const uint8_t index, tlv_t *tlv)
{
    setInteger2TLV(index, tlv);
}

static void setTimestamp2TLV(const uint32_t timetick, tlv_t *tlv)
{
    tlv->tag = VALTYPE_TIMETICKS;
    if (timetick > 0xFFFFFF){
        tlv->len = 4;
        tlv->data[0] = timetick >> 24;
        tlv->data[1] = timetick >> 16;
        tlv->data[2] = timetick >> 8;
        tlv->data[3] = timetick >> 0;
    }
    else if (timetick >0xFFFF){
        tlv->len = 3;
        tlv->data[0] = timetick >> 16;
        tlv->data[1] = timetick >> 8;
        tlv->data[2] = timetick >> 0;
    }
    else if (timetick > 0xFF){
        tlv->len = 2;
        tlv->data[0] = timetick >> 8;
        tlv->data[1] = timetick >> 0;
    }
    else{
        tlv->len = 1;
        tlv->data[0] = timetick >> 0;
    }
}

static void setRequestId2TLV(const uint16_t id, tlv_t *tlv)
{
    tlv->tag = VALTYPE_INT;
    tlv->len = 2;
    tlv->data[0] = id >> 8;
    tlv->data[1] = id >> 0;
}
static void setOneVarBind2TLV(const variable_t *variable, tlv_t *tlv)
{
    tlv_t tmp;
    uint16_t i;
    i = 0;
    tlv->tag = BTAG_SEQ;
    // oid name
    memset(&tmp, 0, sizeof(tmp));
    setOidName2TLV(variable->name, &tmp);
/*
    if (variable->value.type == VALTYPE_IPADDR){
            tmp.data[tmp.len++] = variable->value.data.ipaddr[0];
            tmp.data[tmp.len++] = variable->value.data.ipaddr[1];
            tmp.data[tmp.len++] = variable->value.data.ipaddr[2];
            tmp.data[tmp.len++] = variable->value.data.ipaddr[3];
    }
*/
    i += TLV2Buff(&tmp, tlv->data+i);
    // oid value
    memset(&tmp, 0, sizeof(tmp));
    switch(variable->value.type){
        case VALTYPE_BOOL:{
            memset(&tmp, 0, sizeof(tmp));
            setBool2TLV(variable->value.data.tbool, &tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_INT:{
            memset(&tmp, 0, sizeof(tmp));
            setInteger2TLV(variable->value.data.tint, &tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_OCTSTR:{
            memset(&tmp, 0, sizeof(tmp));
            setString2TLV(variable->value.data.octstr, &tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_OBJID:{
            memset(&tmp, 0, sizeof(tmp));
            setOidName2TLV(variable->value.data.oid, &tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_NULL:{
            memset(&tmp, 0, sizeof(tmp));
            setNull2TLV(&tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_TIMETICKS:{
            memset(&tmp, 0, sizeof(tmp));
            setTimestamp2TLV(variable->value.data.timetick, &tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_IPADDR:{
            memset(&tmp, 0, sizeof(tmp));
            setIpaddr2TLV(variable->value.data.ipaddr, &tmp);
            i += TLV2Buff(&tmp, tlv->data+i);
            break;
        }
        case VALTYPE_ENUM:{
            memset(&tmp, 0, sizeof(tmp));
            break;
        }
        default:{
            break;
        }
    }
    // tlv length
    tlv->len = i;
}

static void setVariableBindings2TLV(const variable_t variables[], uint8_t varcnt, tlv_t *tlv)
{
    tlv_t tmp;
    uint16_t i, j;
    tlv->tag = BTAG_SEQ;
    i = 0;
    for (j = 0; j < varcnt; j++){
        memset(&tmp, 0, sizeof(tmp));
        setOneVarBind2TLV(&variables[j], &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
    }
    tlv->len = i;
}

static void setPDU2TLV(const pdu_t *pdu, tlv_t *tlv)
{
    uint16_t i = 0;
    u8 n=0;
    tlv_t tmp;

    tlv->tag = pdu->type;
    if((pdu->type == PDUTYPE_GET) || (pdu->type == PDUTYPE_GETNEXT) ||
         (pdu->type == PDUTYPE_SET) || (pdu->type == PDUTYPE_RESP)    ||
         (pdu->type == PDUTYPE_TRAP_V2C)){
        // requestid        //2022.5.7
        memset(&tmp, 0, sizeof(tmp));
        if(idLen){
            tmp.tag = VALTYPE_INT;
            tmp.len = idLen;
            for(n=0;n<idLen;n++){
                tmp.data[n] = (u8)(pdu->requestid >> (8*(idLen-n-1)));
            }
            idLen=0;
        }else    setRequestId2TLV(pdu->requestid, &tmp);
        
        i += TLV2Buff(&tmp, tlv->data + i);
        // errstate;
        memset(&tmp, 0, sizeof(tmp));
        setErrorState2TLV(pdu->errorstate, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // errindex;
        memset(&tmp, 0, sizeof(tmp));
        setErrorIndex2TLV(pdu->errorindex, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // variable binding;
        memset(&tmp, 0, sizeof(tmp));
        setVariableBindings2TLV(pdu->variables, pdu->cnt, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
    }
    else if (pdu->type == PDUTYPE_TRAP_V1){
        // enterprise oid
        memset(&tmp, 0, sizeof(tmp));
        setEnterprise2TLV(pdu->variables[1].name, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // agent ip;
        memset(&tmp, 0, sizeof(tmp));
        setIpaddr2TLV(pdu->variables[1].value.data.ipaddr, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // trap type;
        memset(&tmp, 0, sizeof(tmp));
        setTrapType2TLV(pdu->traptype, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // trap specific;
        memset(&tmp, 0, sizeof(tmp));
        setTrapSpecific2TLV(pdu->trapspecific, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // timestamp;
        memset(&tmp, 0, sizeof(tmp));
        setTimestamp2TLV(pdu->variables[0].value.data.timetick, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
        // variable binding;
        memset(&tmp, 0, sizeof(tmp));
        setVariableBindings2TLV(&pdu->variables[2], 1, &tmp);
        i += TLV2Buff(&tmp, tlv->data + i);
    }
    // set tlv length
    tlv->len = i;
}

static int parseTLV(const uint8_t buff[], const uint16_t len, tlv_t *tlv)
{
    int tsize;
    tsize = Buff2TLV(buff, len, tlv);
    if (checkTLV(tlv) == TRUE){
        return tsize;
    }
    return -1;
}

static int parseVersion(const uint8_t data[], const uint16_t len, uint8_t *version)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len == 1) && (tlv.tag == VALTYPE_INT)){
        *version = tlv.data[0];
        return tsize;
    }
    return -1;
}

static int parseCommunity(const uint8_t data[], const uint16_t len, char community[])
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len <= SNMP_MAX_STRING) && (tlv.tag == VALTYPE_OCTSTR)){
        memcpy(community, tlv.data, tlv.len);
        return tsize;
    }
    return -1;
}

static int parseRequestID(const uint8_t data[], const uint16_t len, uint32_t *id)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    idLen = tlv.len;
    if(tlv.tag == VALTYPE_INT && (tlv.len<= 4)){
        int i=0;
        *id=0;
        for(int i=0;i<tlv.len;i++){
            *id = (*id<<8)+tlv.data[i];
        }
        return tsize;
    }
    /*if ((tlv.len <= 2) && (tlv.tag == VALTYPE_INT)){
        *id = tlv.data[0] << 8 | tlv.data[1];
        return tsize;
    }*/
    return -1;
}

static int parseErrorState(const uint8_t data[], const uint16_t len, uint8_t *state)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len == 1) && (tlv.tag == VALTYPE_INT)){
        *state = tlv.data[0];
        return tsize;
    }
    return -1;
}

static int parseErrorIndex(const uint8_t data[], const uint16_t len, uint8_t *index)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len == 1) && (tlv.tag == VALTYPE_INT)){
        *index = tlv.data[0];
        return tsize;
    }
    return -1;
}

static int parseTrapType(const uint8_t data[], const uint16_t len, uint8_t *type)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len == 1) && (tlv.tag == VALTYPE_INT)){
            *type = tlv.data[0];
            return tsize;
    }
    return -1;
}

static int parseTrapSpecific(const uint8_t data[], const uint16_t len, uint8_t *specific)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len == 1) && (tlv.tag == VALTYPE_INT)){
        *specific = tlv.data[0];
        return tsize;
    }
    return -1;
}

static int parseOidName(const uint8_t data[], const uint16_t len, char oidname[])
{
    int oidsize = 0; // sizeof TLV
    tlv_t tlv;
    oidsize =  parseTLV(data, len, &tlv); //
    if ((tlv.len <= SNMP_MAX_STRING) && (tlv.tag == BTAG_OBJID)){
        uint8_t *pdata;
        int remainlen;
        int tsize;
        uint32_t oidint;
        pdata = tlv.data;
        remainlen = tlv.len;

        // "1.x"
        sprintf(oidname,"%u.%u", tlv.data[0]/40, tlv.data[0]%40);
        pdata += 1;
        remainlen -= 1;
        // "1.x.?.?.?.?.?"
        while(remainlen > 0){
                tsize = BERBuff2int(pdata, remainlen, &oidint);
                if (tsize < 0){
                        return -1;
                }
                sprintf(oidname + strlen(oidname), ".%u", oidint);
                pdata += tsize;
                remainlen -= tsize;
        }
        return oidsize;
    }
    return -1;
}

static bool checkOidValueType(const uint8_t type)
{
     switch(type){
         case VALTYPE_BOOL:
         case VALTYPE_INT:
         case VALTYPE_OCTSTR:
         case VALTYPE_OBJID:
         case VALTYPE_NULL:
         case VALTYPE_ENUM:
         case VALTYPE_IPADDR:
         case VALTYPE_TIMETICKS:{
             return TRUE;
         }
     }
     return FALSE;
}

static int parseOidValue(const uint8_t data[], const uint16_t len, value_t *value)
{
    int tsize = 0; // sizeof TLV
    tlv_t tlv;
    tsize =  parseTLV(data, len, &tlv); //
    if (checkOidValueType(tlv.tag) == TRUE){
            value->type = tlv.tag;
            switch(value->type){
                    case VALTYPE_BOOL:{
                            value->data.tbool = tlv.data[0];
                            break;
                    }
                    case VALTYPE_INT:{
                            uint8_t i;
                            value->data.tint = 0;
                            for (i = 0; i < tlv.len; i++){
                                    value->data.tint <<= 8;
                                    value->data.tint |= tlv.data[i];
                            }
                            break;
                    }
                    case VALTYPE_OCTSTR:{
                            memcpy(value->data.octstr, tlv.data, tlv.len);
                            break;
                    }
                    case VALTYPE_OBJID:{
                            parseOidName(tlv.data, tlv.len, value->data.oid);
                            break;
                    }
                    case VALTYPE_NULL:{
                            value->data.tnull = 1;
                            break;
                    }
                    case VALTYPE_TIMETICKS:{
                            uint8_t i;
                            value->data.timetick = 0;
                            for (i = 0; i < tlv.len; i++){
                                    value->data.timetick <<= 8;
                                    value->data.timetick |= tlv.data[i];
                            }
                            break;
                    }
                    case VALTYPE_IPADDR:{
                            value->data.ipaddr[0]= tlv.data[0];
                            value->data.ipaddr[1] = tlv.data[1];
                            value->data.ipaddr[2] = tlv.data[2];
                            value->data.ipaddr[3] = tlv.data[3];
                            break;
                    }
                    case VALTYPE_ENUM:{
                            break;
                    }
                    default:{
                            break;
                    }
            }
            return tsize;
    }
    return -1;
}

static int parseOneVarBind(const uint8_t data[], const uint16_t len, variable_t *variable)
{
    int onevarsize = 0; // sizeof TLV
    tlv_t tlv;
    onevarsize =  parseTLV(data, len, &tlv); //
    if (tlv.tag == BTAG_SEQ){
            uint8_t *pdata;
            int remainlen;
            int tsize;
            pdata = tlv.data;
            remainlen = tlv.len;
            // oid name
            tsize = parseOidName(pdata, remainlen, variable->name);
            if (tsize < 0){
                    return -1;
            }
            pdata += tsize;
            remainlen -= tsize;
            // oid value
            tsize = parseOidValue(pdata, remainlen, &variable->value);
            if (tsize < 0){
                    return -1;
            }
            pdata += tsize;
            remainlen -= tsize;

            return onevarsize;
    }
    return -1;
}

static int parseVaraibleBindings(const uint8_t data[], const uint16_t len, uint8_t *cnt, variable_t variables[])
{
    int varsize = 0; // sizeof TLV
    uint8_t idx = 0;
    tlv_t tlv;
    varsize =  parseTLV(data, len, &tlv); //
    if (tlv.tag == BTAG_SEQ){
            uint8_t *pdata;
            int remainlen;
            int tsize;
            pdata = tlv.data;
            remainlen = tlv.len;

            while(remainlen > 0){
                    tsize = parseOneVarBind(pdata, remainlen, &variables[idx++]);
                    if (tsize < 0){
                            return -1;
                    }
                    pdata += tsize;
                    remainlen -= tsize;
                    *cnt = idx;
            }

            return varsize;
    }

    return -1;
}

static bool checkPDU(const uint8_t type)
{
    switch(type){
     case PDUTYPE_GET:
     case PDUTYPE_GETNEXT:
     case PDUTYPE_RESP:
     case PDUTYPE_SET:
     case PDUTYPE_TRAP_V1:
     case PDUTYPE_TRAP_V2C:{
            return TRUE;
     }
    }
    return FALSE;
}

static int parsePDU(const uint8_t data[], const uint16_t len, pdu_t *pdu)
{
    int pdusize = 0; // sizeof TLV
    tlv_t tlv;
    pdusize =  parseTLV(data, len, &tlv);
    if(checkPDU(tlv.tag) == TRUE){
        uint8_t *pdata;
        int remainlen;
        int tsize;
        pdata = tlv.data;
        remainlen = tlv.len;

        pdu->type = tlv.tag;
        if ((pdu->type == PDUTYPE_GET) || (pdu->type == PDUTYPE_GETNEXT) ||
                (pdu->type == PDUTYPE_SET) || (pdu->type == PDUTYPE_RESP)    ||
                (pdu->type == PDUTYPE_TRAP_V2C)){
                // request id
                tsize = parseRequestID(pdata, remainlen, &pdu->requestid);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // errorstate
                tsize = parseErrorState(pdata, remainlen, &pdu->errorstate);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // errorindex
                tsize = parseErrorIndex(pdata, remainlen, &pdu->errorindex);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                //variables
                tsize = parseVaraibleBindings(pdata, remainlen, &pdu->cnt, pdu->variables);
                if (tsize < 0){
                        return -1;
                }
        }
        else if(pdu->type == PDUTYPE_TRAP_V1){
                // enterprise oid
                tsize = parseOidName(pdata, remainlen, pdu->variables[1].name);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // agent ip;
                tsize = parseOidValue(pdata, remainlen, &pdu->variables[1].value);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // trap type;
                tsize = parseTrapType(pdata, remainlen, &pdu->traptype);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // trap specific;
                tsize = parseTrapSpecific(pdata, remainlen, &pdu->trapspecific);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // timestamp;
                tsize = parseOidValue(pdata, remainlen, &pdu->variables[0].value);
                if (tsize < 0){
                        return -1;
                }
                pdata += tsize;
                remainlen -= tsize;
                // variable binding;
                tsize = parseVaraibleBindings(pdata, remainlen, &pdu->cnt, &pdu->variables[2]);
                if (tsize < 0){
                        return -1;
                }
                pdu->cnt = 3;
        }
        return pdusize;
    }

    return -1;
}

/*===========================================================================*/
/* exported functions.                                                       */
/*===========================================================================*/
int snmplib_package(const snmp_t *snmp, uint8_t buff[], const uint16_t lenofbuff)
{
    uint16_t i = 0;
    tlv_t tlv;
    if ((SNMP_V1 == snmp->version) || (SNMP_V2C == snmp->version)){
        tlv_t tmp;
        // tlv tag flag
        tlv.tag = SNMP_HEAD;
        // version;
        memset(&tmp, 0, sizeof(tmp));
        setVersion2TLV(snmp->version, &tmp);
        i += TLV2Buff(&tmp, tlv.data + i);
        // community;
        memset(&tmp, 0, sizeof(tmp));
        setCommunity2TLV(snmp->community, &tmp);
        i += TLV2Buff(&tmp, tlv.data + i); 
        // pdu
        memset(&tmp, 0, sizeof(tmp));
        setPDU2TLV(&snmp->pdu, &tmp);
        i += TLV2Buff(&tmp, tlv.data + i);
        // tlv length
        tlv.len = i;
    }
    else if (SNMP_V3 == snmp->version){

    }
    if (lenofbuff > (1+2+tlv.len)){
        return TLV2Buff(&tlv, buff);
    }
    else{
        return -1;
    }
}

int snmplib_parse(const uint8_t buff[], const uint16_t lenofbuff, snmp_t *snmp)
{
    int snmpsize = 0;
    tlv_t tlv;
    snmpsize = parseTLV(buff, lenofbuff, &tlv);
    if (tlv.tag == SNMP_HEAD){
        uint8_t *pdata = tlv.data;
        int remainlen = tlv.len;
        int tsize;
        // version
        tsize = parseVersion(pdata, remainlen, &snmp->version);
        if (tsize < 0){
            return -1;
        }
        pdata += tsize;
        remainlen -= tsize;
        // community
        tsize = parseCommunity(pdata, remainlen, snmp->community);
        if (tsize < 0){
            return -1;
        }
        pdata += tsize;
        remainlen -= tsize;
        // pdu
        tsize = parsePDU(pdata, remainlen, &snmp->pdu);
        if (tsize < 0){
            return -1;
        }
        return snmpsize;
    }
    return -1;
}


